Skip to content

Action the review of the agent instruction files - #7347

Merged
trexfeathers merged 10 commits into
SciTools:brownfieldfrom
trexfeathers:brownfield-review-actions
Oct 27, 2026
Merged

trexfeathers merged 10 commits into
SciTools:brownfieldfrom
trexfeathers:brownfield-review-actions

Conversation

@trexfeathers

@trexfeathers trexfeathers commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

(posted by Claude Opus)

Description

Adds a maintainer-notes Sphinx directive, and actions pp-mo's review of the agent instruction files added in #7309. The directive gives module docstrings somewhere to put prose written for a maintainer, so it stays in the file and never reaches the published API page — which is what made "put the explanation in the module docstring" a hard sell. No shipped behaviour changes, so the risk is that a rule now says the wrong thing to future agents, or that the directive hides less than it claims.

The directive is built on docutils and sphinx alone, so it adds no dependency. Its content is never parsed, which means broken markup and unbalanced >>> prompts inside it cannot break a docs build, and the doctest builder ignores them. Getting the name wrong is a build failure rather than a silent leak: make html runs with -W, so an unknown directive stops the build and the content does not render on the way out. A plain reST comment fails the other way round — dedent a single line and the prose is published with a clean build.

Important

One review comment is actioned by deleting a rule rather than qualifying it, which needs a ruling. lib/iris/AGENTS.md banned try/conditional imports in a package __init__.py. That ban sat under Greppability, where it does not belong, since a try-wrapped import of a verbatim name greps perfectly well. It also contradicted the tree in four places: fileformats/__init__.py, io/__init__.py and netcdf/_attribute_handlers.py all defer an iris_grib import, and fileformats/zarr/__init__.py does the same for zarr. I removed the clause and folded the optional-dependency case into the surrounding rule, rather than adding a zarr-shaped exception to a rule four things already break.

Where I would look first, in order of how little I trust myself:

  • The single-exit rule in Size and Shape. pp-mo's exit-flag example is described in prose rather than reproduced as code, so check the description is the shape that was meant.
  • Where each sentence landed in the seventeen converted docstrings. The split is by reader: what a caller needs stays on the page, and rationale, traps, invariants and coupling move into the block. Several paragraphs were split mid-way, keeping the fact above and moving the reason below — zarr/saver.py is the clearest example, where "the default mode is w-" stays and "a Zarr clobber is many non-atomic deletes" moves. If a judgement looks wrong, it is a one-line move to fix.
  • The selection itself. io/format_picker.py had its marker withdrawn with no block added: its docstring is a usage tutorial throughout, and I had marked it on length rather than content. netcdf/saver.py, netcdf/ugrid_load.py, cf/_attribute_handlers.py and io/__init__.py were left alone for the same reason.
  • The doctest exemption in tests/AGENTS.md, drawn at changes confined to lib/iris/tests/ or changelog/.
Verified against a docs build that reads this branch, after a first attempt that did not

The suppression is confirmed by a clean html-noplot build with the API pages. Every maintainer passage checked is absent from generated/api: the import shadowing in zarr, the _CFSaver privacy argument, the attribute-tracking trap in cf.dataset, the non-atomic clobber in zarr.saver, the thin-loop description in zarr.loader. Every user-facing passage checked still renders: the w- default, the one-group-per-call rule, the attributes routing rule, the optional dependency, the import-from-the-package instruction.

The prose does remain in the _modules/ source listings, because sphinx.ext.viewcode publishes module source verbatim. The directive controls the rendered API page, not the published source.

An earlier run of this check was worthless and is worth recording. The environment's editable install resolves iris to a different checkout, so automodule read that tree's docstrings rather than this branch's, and the build proved nothing either way. Re-running with PYTHONPATH pointing at this branch's lib is what produced the result above.

make doctest reports three failing documents — iris.experimental.geovista, iris.loading and user_manual/how_to/mesh_operations. A control run against the unmodified checkout fails on exactly the same three, so they pre-date this branch; the geovista one is an upstream pyvista repr change. None is a module touched here.

An AST comparison that ignores docstrings shows the code of all eighteen changed files is byte-identical to before.

All seven review comments, and what each did to the files
Comment Outcome
Doctests run before finishing Actioned. The "only if you touched a >>> example" carve-out is gone, and the requirement is on the Pre-Finish Checklist as asked.
import * as its own rule Actioned. Split out of the __all__ bullet, with no explanation attached.
iris.fileformats.zarr breaks the __init__.py rules Actioned by deleting the clause, as above.
Multi-level graphs Actioned. Measured first: on Dask 2026.8.0, da.zeros((10000, 10000), chunks=(100, 100)) holds 10000 keys and [:100, :100] of it holds 10001, culled to 2 only at optimize time. The numbers stayed out of the file.
Maintainer information in a comment Answered in the thread at the time. The directive is the eventual answer: maintainer prose now has a home that is not published.
State the purposes of docstrings and comments Actioned. A paragraph naming the three channels and their readers now opens the section.
Guard clauses and early returns Actioned. Of the functions in lib/iris that contain a return, 95% have exactly one, so the old bullet argued against the surrounding code.
Rejected: a zarr exception bullet, a compliance hook, and converting the human-authored NOTE comments

A separate bullet exempting iris.fileformats.zarr was written and then removed. It gave one module the weight of a rule, and zarr is not the exception.

The conversion was originally deferred to a later change, to avoid conflicting with work in flight. It is now done here instead, because the module docstrings on brownfield are complete as of #7346 and leaving markers behind would seed the convention with a reminder rather than an example.

A hook flagging # NOTE: runs longer than three lines was considered, as the only mechanical signal that an agent skipped the block. It would fire on 34 existing sites, all human-authored and pre-dating the convention, so it was dropped.

lib/iris/AGENTS.md ends at 300 lines, meeting the cap its own meta-instruction sets, which it did not before — the branch started at 303. The room came from tightening Module Docstrings Carry the Explanation, where three sentences were said twice: that inline comments scatter an argument and that a side Markdown file rots unseen are both Fast Rule 7, and that misspelling the directive fails the build is in the directive's own module docstring. Two structural savings were considered and dropped: the meta-instruction block is byte-identical in four AGENTS.md files and could become a pointer, but it is too important to demote; the Where Human and Agent Preferences Diverge table mostly restates rules stated elsewhere, but folding it is a change nobody asked for.

pp-mo's review of the .hooks additions on brownfield is still to come, and will be actioned on this branch too.

Checklist

Not applicable to this branch: a What's New entry, type hints, benchmarks and dependencies. Nothing here changes shipped behaviour, and the directive adds no package requirement.

  • Checked if tests need updating
  • Checked if documentation needs updating
  • Confirmed that the GitHub 'checks' on this PR are passing ✅

trexfeathers and others added 2 commits October 26, 2026 13:10
Separate "never import *" from the __all__ rule, and drop the ban on
conditional imports in a package __init__.py. The ban sat under
Greppability, where it does not belong: a try-wrapped import of a verbatim
name greps perfectly well. It also contradicted the tree, where iris_grib
and iris.fileformats.zarr both defer an optional dependency's import.

Record that indexing does not shrink a Dask graph. A slice still references
every chunk of its source, and is culled only at compute.

State the purposes of a docstring, a comment and a module docstring at the
head of the section that governs all three.

Replace the guard-clause advice with a single-exit rule. Nearly every
function in lib/iris that contains a return has exactly one, so the old
wording argued against the surrounding code.

Delete the prose describing the sphinx-needs item a public module docstring
carries. The check_docs_page_metadata hook enforces it, and names the
expected type, the line limit and the missing tag when it fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A doctest asserts real output, so a change anywhere in the library or the
user guide can break one. Nothing in the pytest suite covers them, so the
previous rule - run them only if you edited a >>> example - left those
breakages to CI. Exempt only changes confined to lib/iris/tests/ or to
changelog/, which cannot reach a doctest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.03%. Comparing base (2548395) to head (2226b28).

Additional details and impacted files
@@             Coverage Diff             @@
##           brownfield    #7347   +/-   ##
===========================================
  Coverage       91.03%   91.03%           
===========================================
  Files             106      106           
  Lines           27148    27148           
  Branches         4961     4961           
===========================================
  Hits            24715    24715           
  Misses           1677     1677           
  Partials          756      756           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

trexfeathers and others added 4 commits October 26, 2026 14:24
A module docstring is the only prose read on every visit to a module and
reviewed in the diff, which makes it the right home for subsystem
explanation. Sphinx publishes it, so prose written for a maintainer arrives
in front of a user. This directive drops its content instead, leaving it in
the file and off the page.

Built on docutils and sphinx alone, so it adds no dependency. The content is
never parsed, which makes broken markup and unbalanced doctest prompts safe
to write and keeps the doctest builder away from them. Misspelling the name
stops the build, because make html runs with -W, and the content does not
render on the way out. A plain reST comment fails the other way: dedent one
line and the prose is silently published.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Names the block in the three places lib/iris/AGENTS.md already sends
maintainer-facing prose: the summary of which channel serves which reader,
the brevity rule that sorts a sentence by who it is for, and the home for a
surprise no test can pin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sixteen module docstrings carry maintainer-facing prose - design rationale,
traps, invariants, coupling - and each gains a one-line todo at the point the
maintainer-notes block will go.

Converting them is a separate change. The prose has to be read and sorted,
and several of these files are under active development, so a wide diff here
would conflict with work in flight for no gain.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three of the removed sentences were said twice. That inline comments scatter
an argument, and that a companion Markdown file rots unseen, are both Fast
Rule 7. That misspelling the directive fails the build is in the directive's
own module docstring.

lib/iris/AGENTS.md now fits the 300-line cap its meta-instruction sets,
which it did not before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread lib/iris/AGENTS.md Resolved

@stephenworsley stephenworsley left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review covers the AGENTS.md files. I'm happy with the idea of the maintainer notes.

Comment thread lib/iris/tests/AGENTS.md Resolved
Comment thread lib/iris/tests/AGENTS.md Resolved
Seventeen module docstrings now split by reader. What a caller needs stays on
the page; design rationale, traps, invariants and coupling move into a
maintainer-notes block that Sphinx drops. Two of the seventeen are the saving
modules added by SciTools#7346, which never carried a marker.

iris.io.format_picker loses its marker without gaining a block. Its docstring
is a usage tutorial throughout, so it was marked on length rather than on
content, and nothing in it is written for a maintainer.

Two corrections made in passing, both to text that was being moved anyway.
iris.fileformats.zarr._dataset announced three things about Zarr and then
listed four. iris.fileformats.cf pointed at "the plan, section 8.4"; section 8
of the design document is Risks and has no subsections, so that resolved to
nothing and is dropped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 76a82181-7fe1-4063-9fd7-deb10fb0d658

@stephenworsley stephenworsley left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review covers the module docstrings.

Comment thread lib/iris/fileformats/cf/saver.py Resolved
Comment thread lib/iris/fileformats/netcdf/loader.py Resolved
Comment thread lib/iris/fileformats/zarr/__init__.py Resolved
Comment thread lib/iris/fileformats/zarr/saver.py Resolved
trexfeathers and others added 2 commits October 27, 2026 14:27
The non-atomic clobber behind the "w-" default is a data-loss warning, not a
rationale: a user choosing mode="w" needs to know an interrupted Zarr clobber
destroys the old store without completing the new.

iris.fileformats.netcdf.loader had one thought split across the boundary.
What is left in the module and what moved to the CF loader is one statement,
so it goes back whole, into the block.

Naming only the netCDF saver implied it was the only one; there are two
public _CFSaver subclasses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: ee18e927-ba60-4705-aee1-029036b34706
The run-before-finishing rule keeps one clause of why and drops the rest. The
clause earns its place: without it an agent reasons that its change cannot
reach a doctest and skips the run, which is the rule this replaced.

The checklist item said a doctest you never looked at "is the one that
breaks", which implies one you have looked at is already safe. It now states
what is true instead: nothing in the pytest suite covers them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: ee18e927-ba60-4705-aee1-029036b34706

@stephenworsley stephenworsley left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is mergeable now.

One thing I'd like to highlight here is my comment on the structure of the API docs for zarr.__init__ being less clear than we would like it. This is out of scope for this PR and at the moment is a tradeoff for the sake of making zarr an optional dependency. It's not necessarily a blocker to merging this into main but it's worth recording as a concern.

@trexfeathers
trexfeathers merged commit 2695820 into SciTools:brownfield Oct 27, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants